/*
 * Copyright (c) 1983, 1995, 1996 Eric P. Allman
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *	  may be used to endorse或 promote products derived from this software
 *	  without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * src/port/snprintf.c
 */

#include "c.h"

#include <math.h>

/*
 * 我们以前在这里使用平台的 NL_ARGMAX，但这是个坏主意，
 * 首先，因为这个模块的目的在于消除平台依赖，
 * 而不是延续它们，其次因为某些平台使用荒唐的大值，
 * 导致 dopr() 中栈的过度消耗。
 */
#define PG_NL_ARGMAX 31


/*
 *	SNPRINTF、VSNPRINTF 及相关函数
 *
 * 这些版本是从网上获取的。它们已经被清理过以便正确编译，并添加了
 * 对大多数 C99 规范的支持。剩余未实现的特性有：
 *
 * 1. 无地区设置支持：基数字符始终为 '.'，并且 '
 * （单引号）格式标志被忽略。
 *
 * 2. 不支持 "%n" 格式说明符。
 *
 * 3. 不支持宽字符（"lc" 和 "ls" 格式）。
 *
 * 4. 不支持 "long double"（"Lf" 和相关格式）。
 *
 * 5. 空格和 '#' 标志未实现。
 *
 * 此外，我们支持一些超出 C99 的扩展：
 *
 * 1. 通过 "%n$" 和 "*n$" 进行参数顺序控制，符合 POSIX 要求。
 *
 * 2. "%m" 展开为 strerror(errno) 的值，其中 errno 是
 * 在调用开始时该变量的值。这是一个 glibc 扩展，但非常有用。
 *
 *
 * 历史上，sprintf/snprintf 的结果值在各个平台上各不相同。
 * 该实现现在遵循 C99 标准：
 *
 * 1. 如果在格式字符串中检测到错误，或向目标流写入失败（如 fwrite 报告），
 * 则返回 -1。请注意，超出 snprintf 的目标缓冲区并*不是*错误。
 *
 * 2. 对于成功写入流的情况下，返回写入流的实际字节数。
 *
 * 3. 对于成功的 sprintf/snprintf，返回原本将会写入无限大小缓冲区的字节数
 * （不包括结尾的 '\0'）。snprintf 将输出截断以适合缓冲区
 * （确保结尾有一个 '\0'，除非 count == 0），但这在函数结果中并未反映。
 *
 * snprintf 缓冲区溢出可以通过检查函数结果是否大于或等于提供的 count 来检测。
 */

/**************************************************************
 * 原始：
 * Patrick Powell 1995年4月11日 星期二 09:48:21 PDT
 * 包含一个防炸弹的 doprnt（dopr）版本。
 * 唉。这种事总是很麻烦。请注意，这里的版本不包括浮点数。(现在包括 ...)
 **************************************************************/

/* 防止递归 */
#undef	vsnprintf
#undef	snprintf
#undef	vsprintf
#undef	sprintf
#undef	vfprintf
#undef	fprintf
#undef	vprintf
#undef	printf

/*
 * 我们在一些机器相关的情况下使用平台本地的 snprintf()。
 * 虽然这是 C99 所要求的，但微软的 Visual Studio 在
 * VS2015 之前并没有。幸运的是，实际上我们并不需要长度检查，
 * 所以在该平台上退回到本地 sprintf()。
 */
#if defined(_MSC_VER) && _MSC_VER < 1900	/* pre-VS2015 */
#define snprintf(str,size,...) sprintf(str,__VA_ARGS__)
#endif

/*
 * 有关格式化输出目标的信息。
 *
 * dopr 和子例程不会在 bufend 处写入，但 snprintf
 * 保留一个字节，确保可以在那儿放置尾随的 '\0'。
 *
 * 在 snprintf 中，我们使用 nchars 来计算由于缓冲区溢出而丢弃的字节数。
 * 因此，snprintf 的正确结果为 (bufptr - bufstart) + nchars。 
 * （这并不像看起来那么不一致：nchars 是现在不在缓冲区中的已发出字节数，
 * 要么是因为我们将它们发送到了流，要么是因为我们一开始无法将它们放入缓冲区。）
 */
typedef struct
{
	char	   *bufptr;			/* 下一个缓冲区输出位置 */
	char	   *bufstart;		/* 第一个缓冲区元素 */
	char	   *bufend;			/* 最后+1个缓冲区元素，或者 NULL */
	/* bufend == NULL 是针对 sprintf 的，我们假设 buf 足够大 */
	FILE	   *stream;			/* 最终输出目标，或者 NULL */
	int			nchars;			/* 发送到流的字符数，或丢弃数 */
	bool		failed;			/* 调用失败；errno 被设置 */
} PrintfTarget;

/*
 * 有关格式参数类型和值的信息。请注意，我们
 * 当前不支持“long double”、“wint_t”或“wchar_t *”数据，
 * 也不支持 '%n' 格式代码；否则我们需要更多类型。
 * 此外，在这个级别上，我们无需担心有符号与无符号值。
 */
typedef enum
{
	ATYPE_NONE = 0,
	ATYPE_INT,
	ATYPE_LONG,
	ATYPE_LONGLONG,
	ATYPE_DOUBLE,
	ATYPE_CHARPTR
} PrintfArgType;

typedef union
{
	int			i;
	long		l;
	long long	ll;
	double		d;
	char	   *cptr;
} PrintfArgValue;


static void fc_flushbuffer(PrintfTarget *fc_target);
static void fc_dopr(PrintfTarget *fc_target, const char *fc_format, va_list fc_args);


/*
 * 对外可见的入口点。
 *
 * 所有这些只是 dopr() 的包装。注意在到达 dopr() 之前
 * 绝不能更改 “errno” 的值。
 */

int pg_vsnprintf(char *fc_str, size_t fc_count, const char *fc_fmt, va_list fc_args)
{
	PrintfTarget fc_target;
	char		fc_onebyte[1];

	/*
	 * C99 允许在 count == 0 时 str == NULL 的情况。与其
	 * 在下方特别处理这种情况，不如替换为一个字节的
	 * 本地缓冲区。调用者无法判断，因为函数结果不
	 * 依赖于 count。
	 */
	if (fc_count == 0)
	{
		fc_str = fc_onebyte;
		fc_count = 1;
	}
	fc_target.bufstart = fc_target.bufptr = fc_str;
	fc_target.bufend = fc_str + fc_count - 1;
	fc_target.stream = NULL;
	fc_target.nchars = 0;
	fc_target.failed = false;
	fc_dopr(&fc_target, fc_fmt, fc_args);
	*(fc_target.bufptr) = '\0';
	return fc_target.failed ? -1 : (fc_target.bufptr - fc_target.bufstart
								 + fc_target.nchars);
}

int pg_snprintf(char *fc_str, size_t fc_count, const char *fc_fmt,...)
{
	int			fc_len;
	va_list		fc_args;

	va_start(fc_args, fc_fmt);
	fc_len = pg_vsnprintf(fc_str, fc_count, fc_fmt, fc_args);
	va_end(fc_args);
	return fc_len;
}

int pg_vsprintf(char *fc_str, const char *fc_fmt, va_list fc_args)
{
	PrintfTarget fc_target;

	fc_target.bufstart = fc_target.bufptr = fc_str;
	fc_target.bufend = NULL;
	fc_target.stream = NULL;
	fc_target.nchars = 0;			/* 在这种情况下并不真正使用 */
	fc_target.failed = false;
	fc_dopr(&fc_target, fc_fmt, fc_args);
	*(fc_target.bufptr) = '\0';
	return fc_target.failed ? -1 : (fc_target.bufptr - fc_target.bufstart
								 + fc_target.nchars);
}

int pg_sprintf(char *fc_str, const char *fc_fmt,...)
{
	int			fc_len;
	va_list		fc_args;

	va_start(fc_args, fc_fmt);
	fc_len = pg_vsprintf(fc_str, fc_fmt, fc_args);
	va_end(fc_args);
	return fc_len;
}

int pg_vfprintf(FILE *fc_stream, const char *fc_fmt, va_list fc_args)
{
	PrintfTarget fc_target;
	char		fc_buffer[1024];	/* 大小是任意的 */

	if (fc_stream == NULL)
	{
		errno = EINVAL;
		return -1;
	}
	fc_target.bufstart = fc_target.bufptr = fc_buffer;
	fc_target.bufend = fc_buffer + sizeof(fc_buffer);	/* 使用整个缓冲区 */
	fc_target.stream = fc_stream;
	fc_target.nchars = 0;
	fc_target.failed = false;
	fc_dopr(&fc_target, fc_fmt, fc_args);
	/* 转储任何剩余的缓冲区内容 */
	fc_flushbuffer(&fc_target);
	return fc_target.failed ? -1 : fc_target.nchars;
}

int pg_fprintf(FILE *fc_stream, const char *fc_fmt,...)
{
	int			fc_len;
	va_list		fc_args;

	va_start(fc_args, fc_fmt);
	fc_len = pg_vfprintf(fc_stream, fc_fmt, fc_args);
	va_end(fc_args);
	return fc_len;
}

int pg_vprintf(const char *fc_fmt, va_list fc_args)
{
	return pg_vfprintf(stdout, fc_fmt, fc_args);
}

int pg_printf(const char *fc_fmt,...)
{
	int			fc_len;
	va_list		fc_args;

	va_start(fc_args, fc_fmt);
	fc_len = pg_vfprintf(stdout, fc_fmt, fc_args);
	va_end(fc_args);
	return fc_len;
}

/*
 * 尝试将整个缓冲区写入 target->stream；在任何情况下丢弃整个
 * 缓冲区。仅在 target->stream 被定义时调用此函数。
 */
static void fc_flushbuffer(PrintfTarget *fc_target)
{
	size_t		fc_nc = fc_target->bufptr - fc_target->bufstart;

	/*
	 * 如果我们已经失败，则不要写任何东西；这样可以确保
	 * 我们保留原始失败的 errno。
	 */
	if (!fc_target->failed && fc_nc > 0)
	{
		size_t		fc_written;

		fc_written = fwrite(fc_target->bufstart, 1, fc_nc, fc_target->stream);
		fc_target->nchars += fc_written;
		if (fc_written != fc_nc)
			fc_target->failed = true;
	}
	fc_target->bufptr = fc_target->bufstart;
}


static bool fc_find_arguments(const char *fc_format, va_list fc_args,
						   PrintfArgValue *fc_argvalues);
static void fc_fmtstr(const char *fc_value, int fc_leftjust, int fc_minlen, int fc_maxwidth,
				   int fc_pointflag, PrintfTarget *fc_target);
static void fc_fmtptr(const void *fc_value, PrintfTarget *fc_target);
static void fc_fmtint(long long fc_value, char fc_type, int fc_forcesign,
				   int fc_leftjust, int fc_minlen, int fc_zpad, int fc_precision, int fc_pointflag,
				   PrintfTarget *fc_target);
static void fc_fmtchar(int fc_value, int fc_leftjust, int fc_minlen, PrintfTarget *fc_target);
static void fc_fmtfloat(double fc_value, char fc_type, int fc_forcesign,
					 int fc_leftjust, int fc_minlen, int fc_zpad, int fc_precision, int fc_pointflag,
					 PrintfTarget *fc_target);
static void fc_dostr(const char *fc_str, int fc_slen, PrintfTarget *fc_target);
static void fc_dopr_outch(int fc_c, PrintfTarget *fc_target);
static void fc_dopr_outchmulti(int fc_c, int fc_slen, PrintfTarget *fc_target);
static int	fc_adjust_sign(int fc_is_negative, int fc_forcesign, int *fc_signvalue);
static int	fc_compute_padlen(int fc_minlen, int fc_vallen, int fc_leftjust);
static void fc_leading_pad(int fc_zpad, int fc_signvalue, int *fc_padlen,
						PrintfTarget *fc_target);
static void fc_trailing_pad(int fc_padlen, PrintfTarget *fc_target);

/*
 * 如果 strchrnul 存在（这是一个 glibc 特性），它的速度比
 * 相应的手动循环快得多。如果它不存在，则提供一个替代品。
 *
 * 注意：glibc 将其声明为返回“char *”，但这需要
 * 内部去掉 const，因此我们不遵循该细节。
 */
#ifndef HAVE_STRCHRNUL

static inline const char *
strchrnul(const char *fc_s, int fc_c)
{
	while (*fc_s != '\0' && *fc_s != fc_c)
		fc_s++;
	return fc_s;
}

#else

/*
 * glibc 的 <string.h> 只有在定义了 _GNU_SOURCE 的情况下才声明 strchrnul。
 * 虽然我们通常在 glibc 平台上使用，但配置将设置
 * HAVE_STRCHRNUL，无论它是否被使用。填写缺失的声明，
 * 以便此文件在有或没有 _GNU_SOURCE 的情况下都能干净编译。
 */
#ifndef _GNU_SOURCE
extern char *strchrnul(const char *fc_s, int fc_c);
#endif

#endif							/* HAVE_STRCHRNUL */


/*
 * dopr()：用于所有情况的 *printf 的核心部分。
 */
static void fc_dopr(PrintfTarget *fc_target, const char *fc_format, va_list fc_args)
{
	int			fc_save_errno = errno;
	const char *fc_first_pct = NULL;
	int			fc_ch;
	bool		fc_have_dollar;
	bool		fc_have_star;
	bool		fc_afterstar;
	int			fc_accum;
	int			fc_longlongflag;
	int			fc_longflag;
	int			fc_pointflag;
	int			fc_leftjust;
	int			fc_fieldwidth;
	int			fc_precision;
	int			fc_zpad;
	int			fc_forcesign;
	int			fc_fmtpos;
	int			fc_cvalue;
	long long	fc_numvalue;
	double		fc_fvalue;
	const char *fc_strvalue;
	PrintfArgValue fc_argvalues[PG_NL_ARGMAX + 1];

	/*
	 * 最初，我们假设格式字符串不使用 %n$。第一次遇到带有该规范的转换说明时，我们将调用
	 * find_arguments() 来检查 %n$ 的一致性使用，并根据正确的顺序填充 argvalues 数组中的参数值。
	 */
	fc_have_dollar = false;

	while (*fc_format != '\0')
	{
		/* 定位下一个转换说明符 */
		if (*fc_format != '%')
		{
			/* 扫描到下一个 '%' 或字符串末尾 */
			const char *fc_next_pct = strchrnul(fc_format + 1, '%');

			/* 转储我们刚刚扫描的文字数据 */
			fc_dostr(fc_format, fc_next_pct - fc_format, fc_target);
			if (fc_target->failed)
				break;

			if (*fc_next_pct == '\0')
				break;
			fc_format = fc_next_pct;
		}

		/*
		 * 记住第一个转换说明符的起始位置；如果我们找到 %n$，那么
		 * find_arguments() 从这里开始是足够的，无需重新扫描
		 * 之前的文字文本。
		 */
		if (fc_first_pct == NULL)
			fc_first_pct = fc_format;

		/* 从 *format 开始处理转换说明符 */
		fc_format++;

		/* 对于严格为 %s 的转换说明符的快速路径 */
		if (*fc_format == 's')
		{
			fc_format++;
			fc_strvalue = va_arg(fc_args, char *);
			if (fc_strvalue == NULL)
				fc_strvalue = "(null)";
			fc_dostr(fc_strvalue, strlen(fc_strvalue), fc_target);
			if (fc_target->failed)
				break;
			continue;
		}

		fc_fieldwidth = fc_precision = fc_zpad = fc_leftjust = fc_forcesign = 0;
		fc_longflag = fc_longlongflag = fc_pointflag = 0;
		fc_fmtpos = fc_accum = 0;
		fc_have_star = fc_afterstar = false;
nextch2:
		fc_ch = *fc_format++;
		switch (fc_ch)
		{
			case '-':
				fc_leftjust = 1;
				goto nextch2;
			case '+':
				fc_forcesign = 1;
				goto nextch2;
			case '0':
				/* 如果尚未有非零数字，则设置零填充 */
				if (fc_accum == 0 && !fc_pointflag)
					fc_zpad = '0';
				/* FALL THRU */
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				fc_accum = fc_accum * 10 + (fc_ch - '0');
				goto nextch2;
			case '.':
				if (fc_have_star)
					fc_have_star = false;
				else
					fc_fieldwidth = fc_accum;
				fc_pointflag = 1;
				fc_accum = 0;
				goto nextch2;
			case '*':
				if (fc_have_dollar)
				{
					/*
					 * 我们将在读取 n$ 后处理值。注意假设 have_dollar 设置正确是可以的，因为在有效的
					 * 格式字符串中，初始 % 必须有 n$（如果有 *）。
					 */
					fc_afterstar = true;
				}
				else
				{
					/* 现在获取并处理值 */
					int			fc_starval = va_arg(fc_args, int);

					if (fc_pointflag)
					{
						fc_precision = fc_starval;
						if (fc_precision < 0)
						{
							fc_precision = 0;
							fc_pointflag = 0;
						}
					}
					else
					{
						fc_fieldwidth = fc_starval;
						if (fc_fieldwidth < 0)
						{
							fc_leftjust = 1;
							fc_fieldwidth = -fc_fieldwidth;
						}
					}
				}
				fc_have_star = true;
				fc_accum = 0;
				goto nextch2;
			case '$':
				/* 第一个美元符号？ */
				if (!fc_have_dollar)
				{
					/* 是的，检查格式中的所有转换说明符 */
					if (!fc_find_arguments(fc_first_pct, fc_args, fc_argvalues))
						goto bad_format;
					fc_have_dollar = true;
				}
				if (fc_afterstar)
				{
					/* 获取并处理星号值 */
					int			fc_starval = fc_argvalues[fc_accum].i;

					if (fc_pointflag)
					{
						fc_precision = fc_starval;
						if (fc_precision < 0)
						{
							fc_precision = 0;
							fc_pointflag = 0;
						}
					}
					else
					{
						fc_fieldwidth = fc_starval;
						if (fc_fieldwidth < 0)
						{
							fc_leftjust = 1;
							fc_fieldwidth = -fc_fieldwidth;
						}
					}
					fc_afterstar = false;
				}
				else
					fc_fmtpos = fc_accum;
				fc_accum = 0;
				goto nextch2;
			case 'l':
				if (fc_longflag)
					fc_longlongflag = 1;
				else
					fc_longflag = 1;
				goto nextch2;
			case 'z':
#if SIZEOF_SIZE_T == 8
#ifdef HAVE_LONG_INT_64
				fc_longflag = 1;
#elif defined(HAVE_LONG_LONG_INT_64)
				fc_flonglongflag = 1;
#else
#error "Don't know how to print 64bit integers"
#endif
#else
				/* 假设 size_t 与 int 的大小相同 */
#endif
				goto nextch2;
			case 'h':
			case '\'':
				/* 忽略这些 */
				goto nextch2;
			case 'd':
			case 'i':
				if (!fc_have_star)
				{
					if (fc_pointflag)
						fc_precision = fc_accum;
					else
						fc_fieldwidth = fc_accum;
				}
				if (fc_have_dollar)
				{
					if (fc_longlongflag)
						fc_numvalue = fc_argvalues[fc_fmtpos].ll;
					else if (fc_longflag)
						fc_numvalue = fc_argvalues[fc_fmtpos].l;
					else
						fc_numvalue = fc_argvalues[fc_fmtpos].i;
				}
				else
				{
					if (fc_longlongflag)
						fc_numvalue = va_arg(fc_args, long long);
					else if (fc_longflag)
						fc_numvalue = va_arg(fc_args, long);
					else
						fc_numvalue = va_arg(fc_args, int);
				}
				fc_fmtint(fc_numvalue, fc_ch, fc_forcesign, fc_leftjust, fc_fieldwidth, fc_zpad,
					   fc_precision, fc_pointflag, fc_target);
				break;
			case 'o':
			case 'u':
			case 'x':
			case 'X':
				if (!fc_have_star)
				{
					if (fc_pointflag)
						fc_precision = fc_accum;
					else
						fc_fieldwidth = fc_accum;
				}
				if (fc_have_dollar)
				{
					if (fc_longlongflag)
						fc_numvalue = (unsigned long long) fc_argvalues[fc_fmtpos].ll;
					else if (fc_longflag)
						fc_numvalue = (unsigned long) fc_argvalues[fc_fmtpos].l;
					else
						fc_numvalue = (unsigned int) fc_argvalues[fc_fmtpos].i;
				}
				else
				{
					if (fc_longlongflag)
						fc_numvalue = (unsigned long long) va_arg(fc_args, long long);
					else if (fc_longflag)
						fc_numvalue = (unsigned long) va_arg(fc_args, long);
					else
						fc_numvalue = (unsigned int) va_arg(fc_args, int);
				}
				fc_fmtint(fc_numvalue, fc_ch, fc_forcesign, fc_leftjust, fc_fieldwidth, fc_zpad,
					   fc_precision, fc_pointflag, fc_target);
				break;
			case 'c':
				if (!fc_have_star)
				{
					if (fc_pointflag)
						fc_precision = fc_accum;
					else
						fc_fieldwidth = fc_accum;
				}
				if (fc_have_dollar)
					fc_cvalue = (unsigned char) fc_argvalues[fc_fmtpos].i;
				else
					fc_cvalue = (unsigned char) va_arg(fc_args, int);
				fc_fmtchar(fc_cvalue, fc_leftjust, fc_fieldwidth, fc_target);
				break;
			case 's':
				if (!fc_have_star)
				{
					if (fc_pointflag)
						fc_precision = fc_accum;
					else
						fc_fieldwidth = fc_accum;
				}
				if (fc_have_dollar)
					fc_strvalue = fc_argvalues[fc_fmtpos].cptr;
				else
					fc_strvalue = va_arg(fc_args, char *);
				/* 如果字符串为 NULL，默默替换为 "(null)" */
				if (fc_strvalue == NULL)
					fc_strvalue = "(null)";
				fc_fmtstr(fc_strvalue, fc_leftjust, fc_fieldwidth, fc_precision, fc_pointflag,
					   fc_target);
				break;
			case 'p':
				/* fieldwidth/leftjust 被忽略 ... */
				if (fc_have_dollar)
					fc_strvalue = fc_argvalues[fc_fmtpos].cptr;
				else
					fc_strvalue = va_arg(fc_args, char *);
				fc_fmtptr((const void *) fc_strvalue, fc_target);
				break;
			case 'e':
			case 'E':
			case 'f':
			case 'g':
			case 'G':
				if (!fc_have_star)
				{
					if (fc_pointflag)
						fc_precision = fc_accum;
					else
						fc_fieldwidth = fc_accum;
				}
				if (fc_have_dollar)
					fc_fvalue = fc_argvalues[fc_fmtpos].d;
				else
					fc_fvalue = va_arg(fc_args, double);
				fc_fmtfloat(fc_fvalue, fc_ch, fc_forcesign, fc_leftjust,
						 fc_fieldwidth, fc_zpad,
						 fc_precision, fc_pointflag,
						 fc_target);
				break;
			case 'm':
				{
					char		fc_errbuf[PG_STRERROR_R_BUFLEN];
					const char *fc_errm = strerror_r(fc_save_errno,
												  fc_errbuf, sizeof(fc_errbuf));

					fc_dostr(fc_errm, strlen(fc_errm), fc_target);
				}
				break;
			case '%':
				fc_dopr_outch('%', fc_target);
				break;
			default:

				/*
				 * 其他任何东西——特别是 '\0' 表示格式字符串的结束——都是无效的。
				 */
				goto bad_format;
		}

		/* 在每个转换说明符后检查失败 */
		if (fc_target->failed)
			break;
	}

	return;

bad_format:
	errno = EINVAL;
	fc_target->failed = true;
}

/*
 * find_arguments()：为带有 %n$ 的格式说明符整理参数
 *
 * 如果格式有效，返回 true 并将 argvalues[i] 填充为具有 %i$ 或 *i$ 的转换说明符的值。
 * 否则返回 false。
 */
static bool fc_find_arguments(const char *fc_format, va_list fc_args,
			   PrintfArgValue *fc_argvalues)
{
	int			fc_ch;
	bool		fc_afterstar;
	int			fc_accum;
	int			fc_longlongflag;
	int			fc_longflag;
	int			fc_fmtpos;
	int			fc_i;
	int			fc_last_dollar;
	PrintfArgType fc_argtypes[PG_NL_ARGMAX + 1];

	/* 初始化为 "没有已知的美元参数" */
	fc_last_dollar = 0;
	MemSet(fc_argtypes, 0, sizeof(fc_argtypes));

	/*
	 * 该循环必须接受与 dopr() 中相同的格式字符串。
	 * 然而，我们不需要以相同的细节级别分析它们。
	 *
	 * 由于我们仅在某处有美元类型说明符时才被调用，因此如果发现非美元说明符，我们可以立即失败。
	 * 根据 C99 标准，格式字符串中的所有参数引用必须是其中之一。
	 */
	while (*fc_format != '\0')
	{
		/* 定位下一个转换说明符 */
		if (*fc_format != '%')
		{
			/* 与 dopr 不同，如果没有更多说明符，我们可以直接退出 */
			fc_format = strchr(fc_format + 1, '%');
			if (fc_format == NULL)
				break;
		}

		/* 从 *format 开始处理转换说明符 */
		fc_format++;
		fc_longflag = fc_longlongflag = 0;
		fc_fmtpos = fc_accum = 0;
		fc_afterstar = false;
nextch1:
		fc_ch = *fc_format++;
		switch (fc_ch)
		{
			case '-':
			case '+':
				goto nextch1;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				fc_accum = fc_accum * 10 + (fc_ch - '0');
				goto nextch1;
			case '.':
				fc_accum = 0;
				goto nextch1;
			case '*':
				if (fc_afterstar)
					return false;	/* 先前的星号缺少美元 */
				fc_afterstar = true;
				fc_accum = 0;
				goto nextch1;
			case '$':
				if (fc_accum <= 0 || fc_accum > PG_NL_ARGMAX)
					return false;
				if (fc_afterstar)
				{
					if (fc_argtypes[fc_accum] &&
						fc_argtypes[fc_accum] != ATYPE_INT)
						return false;
					fc_argtypes[fc_accum] = ATYPE_INT;
					fc_last_dollar = Max(fc_last_dollar, fc_accum);
					fc_afterstar = false;
				}
				else
					fc_fmtpos = fc_accum;
				fc_accum = 0;
				goto nextch1;
			case 'l':
				if (fc_longflag)
					fc_longlongflag = 1;
				else
					fc_longflag = 1;
				goto nextch1;
			case 'z':
#if SIZEOF_SIZE_T == 8
#ifdef HAVE_LONG_INT_64
				fc_longflag = 1;
#elif defined(HAVE_LONG_LONG_INT_64)
				fc_longlongflag = 1;
#else
#error "Don't know how to print 64bit integers"
#endif
#else
				/* 假设 size_t 与 int 的大小相同 */
#endif
				goto nextch1;
			case 'h':
			case '\'':
				/* 忽略这些 */
				goto nextch1;
			case 'd':
			case 'i':
			case 'o':
			case 'u':
			case 'x':
			case 'X':
				if (fc_fmtpos)
				{
					PrintfArgType fc_atype;

					if (fc_longlongflag)
						fc_atype = ATYPE_LONGLONG;
					else if (fc_longflag)
						fc_atype = ATYPE_LONG;
					else
						fc_atype = ATYPE_INT;
					if (fc_argtypes[fc_fmtpos] &&
						fc_argtypes[fc_fmtpos] != fc_atype)
						return false;
					fc_argtypes[fc_fmtpos] = fc_atype;
					fc_last_dollar = Max(fc_last_dollar, fc_fmtpos);
				}
				else
					return false;	/* 非美元转换说明符 */
				break;
			case 'c':
				if (fc_fmtpos)
				{
					if (fc_argtypes[fc_fmtpos] &&
						fc_argtypes[fc_fmtpos] != ATYPE_INT)
						return false;
					fc_argtypes[fc_fmtpos] = ATYPE_INT;
					fc_last_dollar = Max(fc_last_dollar, fc_fmtpos);
				}
				else
					return false;	/* 非美元转换说明符 */
				break;
			case 's':
			case 'p':
				if (fc_fmtpos)
				{
					if (fc_argtypes[fc_fmtpos] &&
						fc_argtypes[fc_fmtpos] != ATYPE_CHARPTR)
						return false;
					fc_argtypes[fc_fmtpos] = ATYPE_CHARPTR;
					fc_last_dollar = Max(fc_last_dollar, fc_fmtpos);
				}
				else
					return false;	/* 非美元转换说明符 */
				break;
			case 'e':
			case 'E':
			case 'f':
			case 'g':
			case 'G':
				if (fc_fmtpos)
				{
					if (fc_argtypes[fc_fmtpos] &&
						fc_argtypes[fc_fmtpos] != ATYPE_DOUBLE)
						return false;
					fc_argtypes[fc_fmtpos] = ATYPE_DOUBLE;
					fc_last_dollar = Max(fc_last_dollar, fc_fmtpos);
				}
				else
					return false;	/* 非美元转换说明符 */
				break;
			case 'm':
			case '%':
				break;
			default:
				return false;	/* 无效的格式字符串 */
		}

		/*
		 * 如果我们在后面星号仍然设置的情况下完成说明符，里面就有一个
		 * 非美元星号。
		 */
		if (fc_afterstar)
			return false;		/* 非美元转换说明符 */
	}

	/*
	 * 格式目前看起来有效，因此按物理顺序收集参数。
	 * （因为我们拒绝了任何非美元规格，这些规格本会收集参数，
	 * 我们知道 dopr() 还没有收集任何参数。）
	 */
	for (fc_i = 1; fc_i <= fc_last_dollar; fc_i++)
	{
		switch (fc_argtypes[fc_i])
		{
			case ATYPE_NONE:
				return false;
			case ATYPE_INT:
				fc_argvalues[fc_i].i = va_arg(fc_args, int);
				break;
			case ATYPE_LONG:
				fc_argvalues[fc_i].l = va_arg(fc_args, long);
				break;
			case ATYPE_LONGLONG:
				fc_argvalues[fc_i].ll = va_arg(fc_args, long long);
				break;
			case ATYPE_DOUBLE:
				fc_argvalues[fc_i].d = va_arg(fc_args, double);
				break;
			case ATYPE_CHARPTR:
				fc_argvalues[fc_i].cptr = va_arg(fc_args, char *);
				break;
		}
	}

	return true;
}

static void fc_fmtstr(const char *fc_value, int fc_leftjust, int fc_minlen, int fc_maxwidth,
	   int fc_pointflag, PrintfTarget *fc_target)
{
	int			fc_padlen,
				fc_vallen;			/* 要填充的数量 */

	/*
	 * 如果指定了最大宽度（精度），我们必须不获取超过这个值的字节。
	 */
	if (fc_pointflag)
		fc_vallen = strnlen(fc_value, fc_maxwidth);
	else
		fc_vallen = strlen(fc_value);

	fc_padlen = fc_compute_padlen(fc_minlen, fc_vallen, fc_leftjust);

	if (fc_padlen > 0)
	{
		fc_dopr_outchmulti(' ', fc_padlen, fc_target);
		fc_padlen = 0;
	}

	fc_dostr(fc_value, fc_vallen, fc_target);

	fc_trailing_pad(fc_padlen, fc_target);
}

static void fc_fmtptr(const void *fc_value, PrintfTarget *fc_target)
{
	int			fc_vallen;
	char		fc_convert[64];

	/* 我们依赖常规 C 库的 snprintf 来进行基本转换 */
	fc_vallen = snprintf(fc_convert, sizeof(fc_convert), "%p", fc_value);
	if (fc_vallen < 0)
		fc_target->failed = true;
	else
		fc_dostr(fc_convert, fc_vallen, fc_target);
}

static void fc_fmtint(long long fc_value, char fc_type, int fc_forcesign, int fc_leftjust,
	   int fc_minlen, int fc_zpad, int fc_precision, int fc_pointflag,
	   PrintfTarget *fc_target)
{
	unsigned long long fc_uvalue;
	int			fc_base;
	int			fc_dosign;
	const char *fc_cvt = "0123456789abcdef";
	int			fc_signvalue = 0;
	char		fc_convert[64];
	int			fc_vallen = 0;
	int			fc_padlen;			/* 要填充的数量 */
	int			fc_zeropad;		/* 额外的前导零 */

	switch (fc_type)
	{
		case 'd':
		case 'i':
			fc_base = 10;
			fc_dosign = 1;
			break;
		case 'o':
			fc_base = 8;
			fc_dosign = 0;
			break;
		case 'u':
			fc_base = 10;
			fc_dosign = 0;
			break;
		case 'x':
			fc_base = 16;
			fc_dosign = 0;
			break;
		case 'X':
			fc_cvt = "0123456789ABCDEF";
			fc_base = 16;
			fc_dosign = 0;
			break;
		default:
			return;				/* 保持编译器安静 */
	}

	/* 禁用 MSVC 关于将一元减法应用于无符号值的警告 */
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4146)
#endif
	/* 处理 +/- */
	if (fc_dosign && fc_adjust_sign((fc_value < 0), fc_forcesign, &fc_signvalue))
		fc_uvalue = -(unsigned long long) fc_value;
	else
		fc_uvalue = (unsigned long long) fc_value;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

	/*
	 * SUS：用显式精度为 0 转换 0 的结果是没有字符
	 */
	if (fc_value == 0 && fc_pointflag && fc_precision == 0)
		fc_vallen = 0;
	else
	{
		/*
		 * 将整数转换为字符串。我们对每个可能的基值进行特殊处理，
		 * 以避免一般性除法。在大多数机器上，固定常量的除法
		 * 可以比一般的除法便宜得多。
		 */
		if (fc_base == 10)
		{
			do
			{
				fc_convert[sizeof(fc_convert) - (++fc_vallen)] = fc_cvt[fc_uvalue % 10];
				fc_uvalue = fc_uvalue / 10;
			} while (fc_uvalue);
		}
		else if (fc_base == 16)
		{
			do
			{
				fc_convert[sizeof(fc_convert) - (++fc_vallen)] = fc_cvt[fc_uvalue % 16];
				fc_uvalue = fc_uvalue / 16;
			} while (fc_uvalue);
		}
		else					/* 基数 == 8 */
		{
			do
			{
				fc_convert[sizeof(fc_convert) - (++fc_vallen)] = fc_cvt[fc_uvalue % 8];
				fc_uvalue = fc_uvalue / 8;
			} while (fc_uvalue);
		}
	}

	fc_zeropad = Max(0, fc_precision - fc_vallen);

	fc_padlen = fc_compute_padlen(fc_minlen, fc_vallen + fc_zeropad, fc_leftjust);

	fc_leading_pad(fc_zpad, fc_signvalue, &fc_padlen, fc_target);

	if (fc_zeropad > 0)
		fc_dopr_outchmulti('0', fc_zeropad, fc_target);

	fc_dostr(fc_convert + sizeof(fc_convert) - fc_vallen, fc_vallen, fc_target);

	fc_trailing_pad(fc_padlen, fc_target);
}

static void fc_fmtchar(int fc_value, int fc_leftjust, int fc_minlen, PrintfTarget *fc_target)
{
	int			fc_padlen;			/* 要填充的数量 */

	fc_padlen = fc_compute_padlen(fc_minlen, 1, fc_leftjust);

	if (fc_padlen > 0)
	{
		fc_dopr_outchmulti(' ', fc_padlen, fc_target);
		fc_padlen = 0;
	}

	fc_dopr_outch(fc_value, fc_target);

	fc_trailing_pad(fc_padlen, fc_target);
}

static void fc_fmtfloat(double fc_value, char fc_type, int fc_forcesign, int fc_leftjust,
		 int fc_minlen, int fc_zpad, int fc_precision, int fc_pointflag,
		 PrintfTarget *fc_target)
{
	int			fc_signvalue = 0;
	int			fc_prec;
	int			fc_vallen;
	char		fc_fmt[8];
	char		fc_convert[1024];
	int			fc_zeropadlen = 0; /* 用零填充的数量 */
	int			fc_padlen;			/* 用空格填充的数量 */

	/*
	 * 我们依赖常规 C 库的 snprintf 来进行基本转换，
	 * 然后在这里处理填充问题。
	 *
	 * “double”的动态范围大约为 1E+-308，符合 IEEE 数学，
	 * 其他硬件也不会差太多。在“f”格式中，snprintf
	 * 因此最多可以在小数点左侧生成 308 个字符；
	 * 而我们需要允许精度高达 308+17，以确保不会从非常小的值中截断有效数字。
	 * 为了处理这两个极端，我们使用 1024 字节的缓冲区，并限制请求的精度为 350 位；
	 * 这应该可以防止即使在非 IEEE 数学中也出现缓冲区溢出。
	 * 如果原始精度请求超过 350，则另外用零进行填充。
	 *
	 * 我们特别处理无穷大和 NaN，以确保输出与平台无关。
	 */
	if (fc_precision < 0)			/* 避免“accum”的可能溢出 */
		fc_precision = 0;
	fc_prec = Min(fc_precision, 350);

	if (isnan(fc_value))
	{
		strcpy(fc_convert, "NaN");
		fc_vallen = 3;
		/* 无零填充，无论精度规格如何 */
	}
	else
	{
		/*
		 * 处理符号（NaN 没有符号，因此在上面的情况下我们不这样做）。
		 * “value < 0.0” 对于 IEEE 负零来说并不真实，
		 * 所以我们通过查看 value 等于 0.0
		 *（根据 ==）但不根据 memcmp 来检测。
		 */
		static const double fc_dzero = 0.0;

		if (fc_adjust_sign((fc_value < 0.0 ||
						 (fc_value == 0.0 &&
						  memcmp(&fc_value, &fc_dzero, sizeof(double)) != 0)),
						fc_forcesign, &fc_signvalue))
			fc_value = -fc_value;

		if (isinf(fc_value))
		{
			strcpy(fc_convert, "Infinity");
			fc_vallen = 8;
			/* 无零填充，无论精度规格如何 */
		}
		else if (fc_pointflag)
		{
			fc_zeropadlen = fc_precision - fc_prec;
			fc_fmt[0] = '%';
			fc_fmt[1] = '.';
			fc_fmt[2] = '*';
			fc_fmt[3] = fc_type;
			fc_fmt[4] = '\0';
			fc_vallen = snprintf(fc_convert, sizeof(fc_convert), fc_fmt, fc_prec, fc_value);
		}
		else
		{
			fc_fmt[0] = '%';
			fc_fmt[1] = fc_type;
			fc_fmt[2] = '\0';
			fc_vallen = snprintf(fc_convert, sizeof(fc_convert), fc_fmt, fc_value);
		}
		if (fc_vallen < 0)
			goto fail;

		/*
		 * 唯一支持的平台 Windows，即使两个数字就足够，
		 * 也喜欢输出三位数的指数字段。对这样的结果进行黑客处理，
		 * 使其看起来像其他人。
		 */
#ifdef WIN32
		if (vallen >= 6 &&
			convert[vallen - 5] == 'e' &&
			convert[vallen - 3] == '0')
		{
			convert[vallen - 3] = convert[vallen - 2];
			convert[vallen - 2] = convert[vallen - 1];
			vallen--;
		}
#endif
	}

	fc_padlen = fc_compute_padlen(fc_minlen, fc_vallen + fc_zeropadlen, fc_leftjust);

	fc_leading_pad(fc_zpad, fc_signvalue, &fc_padlen, fc_target);

	if (fc_zeropadlen > 0)
	{
		/* 如果是 'e' 或 'E' 格式，在指数前插入零 */
		char	   *fc_epos = strrchr(fc_convert, 'e');

		if (!fc_epos)
			fc_epos = strrchr(fc_convert, 'E');
		if (fc_epos)
		{
			/* 在指数前填充 */
			fc_dostr(fc_convert, fc_epos - fc_convert, fc_target);
			fc_dopr_outchmulti('0', fc_zeropadlen, fc_target);
			fc_dostr(fc_epos, fc_vallen - (fc_epos - fc_convert), fc_target);
		}
		else
		{
			/* 没有指数，在数字后填充 */
			fc_dostr(fc_convert, fc_vallen, fc_target);
			fc_dopr_outchmulti('0', fc_zeropadlen, fc_target);
		}
	}
	else
	{
		/* 无零填充，直接输出数字 */
		fc_dostr(fc_convert, fc_vallen, fc_target);
	}

	fc_trailing_pad(fc_padlen, fc_target);
	return;

fail:
	fc_target->failed = true;
}

/*
 * 非标准入口点以高效打印双精度值。
 *
 * 这大致相当于 strfromd()，但其 API 更适合 float8out() 的要求。
 * 行为类似于 snprintf()，格式为 "%.ng"，其中 n 是指定的精度。
 * 然而，目标缓冲区必须是非空的（即 count > 0），并且
 * 精度被默默限制在合理范围内。
 */
int pg_strfromd(char *fc_str, size_t fc_count, int fc_precision, double fc_value)
{
	PrintfTarget fc_target;
	int			fc_signvalue = 0;
	int			fc_vallen;
	char		fc_fmt[8];
	char		fc_convert[64];

	/* 像 pg_snprintf 一样设置目标，但要求缓冲区非空 */
	Assert(fc_count > 0);
	fc_target.bufstart = fc_target.bufptr = fc_str;
	fc_target.bufend = fc_str + fc_count - 1;
	fc_target.stream = NULL;
	fc_target.nchars = 0;
	fc_target.failed = false;

	/*
	 * 我们将精度限制在合理范围内；这一点与使用 "g" 格式而不进行填充的知识相结合，
	 * 使得 convert[] 缓冲区可以相对较小。
	 */
	if (fc_precision < 1)
		fc_precision = 1;
	else if (fc_precision > 32)
		fc_precision = 32;

	/*
	 * 其余部分只是以上 fmtfloat() 逻辑的内联版本，
	 * 简化利用了不需要填充的知识。
	 */
	if (isnan(fc_value))
	{
		strcpy(fc_convert, "NaN");
		fc_vallen = 3;
	}
	else
	{
		static const double fc_dzero = 0.0;

		if (fc_value < 0.0 ||
			(fc_value == 0.0 &&
			 memcmp(&fc_value, &fc_dzero, sizeof(double)) != 0))
		{
			fc_signvalue = '-';
			fc_value = -fc_value;
		}

		if (isinf(fc_value))
		{
			strcpy(fc_convert, "Infinity");
			fc_vallen = 8;
		}
		else
		{
			fc_fmt[0] = '%';
			fc_fmt[1] = '.';
			fc_fmt[2] = '*';
			fc_fmt[3] = 'g';
			fc_fmt[4] = '\0';
			fc_vallen = snprintf(fc_convert, sizeof(fc_convert), fc_fmt, fc_precision, fc_value);
			if (fc_vallen < 0)
			{
				fc_target.failed = true;
				goto fail;
			}

#ifdef WIN32
			if (vallen >= 6 &&
				convert[vallen - 5] == 'e' &&
				convert[vallen - 3] == '0')
			{
				convert[vallen - 3] = convert[vallen - 2];
				convert[vallen - 2] = convert[vallen - 1];
				vallen--;
			}
#endif
		}
	}

	if (fc_signvalue)
		fc_dopr_outch(fc_signvalue, &fc_target);

	fc_dostr(fc_convert, fc_vallen, &fc_target);

fail:
	*(fc_target.bufptr) = '\0';
	return fc_target.failed ? -1 : (fc_target.bufptr - fc_target.bufstart
								 + fc_target.nchars);
}


static void fc_dostr(const char *fc_str, int fc_slen, PrintfTarget *fc_target)
{
	/* slen == 1 的常见情况的快速路径 */
	if (fc_slen == 1)
	{
		fc_dopr_outch(*fc_str, fc_target);
		return;
	}

	while (fc_slen > 0)
	{
		int			fc_avail;

		if (fc_target->bufend != NULL)
			fc_avail = fc_target->bufend - fc_target->bufptr;
		else
			fc_avail = fc_slen;
		if (fc_avail <= 0)
		{
			/* 缓冲区满了，我们可以转储到流吗？ */
			if (fc_target->stream == NULL)
			{
				fc_target->nchars += fc_slen; /* 不可以，丢失数据 */
				return;
			}
			fc_flushbuffer(fc_target);
			continue;
		}
		fc_avail = Min(fc_avail, fc_slen);
		memmove(fc_target->bufptr, fc_str, fc_avail);
		fc_target->bufptr += fc_avail;
		fc_str += fc_avail;
		fc_slen -= fc_avail;
	}
}

static void fc_dopr_outch(int fc_c, PrintfTarget *fc_target)
{
	if (fc_target->bufend != NULL && fc_target->bufptr >= fc_target->bufend)
	{
		/* 缓冲区满了，我们可以转储到流吗？ */
		if (fc_target->stream == NULL)
		{
			fc_target->nchars++;	/* 不可以，丢失数据 */
			return;
		}
		fc_flushbuffer(fc_target);
	}
	*(fc_target->bufptr++) = fc_c;
}

static void fc_dopr_outchmulti(int fc_c, int fc_slen, PrintfTarget *fc_target)
{
	/* slen == 1 的常见情况的快速路径 */
	if (fc_slen == 1)
	{
		fc_dopr_outch(fc_c, fc_target);
		return;
	}

	while (fc_slen > 0)
	{
		int			fc_avail;

		if (fc_target->bufend != NULL)
			fc_avail = fc_target->bufend - fc_target->bufptr;
		else
			fc_avail = fc_slen;
		if (fc_avail <= 0)
		{
			/* 缓冲区满了，我们可以转储到流吗？ */
			if (fc_target->stream == NULL)
			{
				fc_target->nchars += fc_slen; /* 不可以，丢失数据 */
				return;
			}
			fc_flushbuffer(fc_target);
			continue;
		}
		fc_avail = Min(fc_avail, fc_slen);
		memset(fc_target->bufptr, fc_c, fc_avail);
		fc_target->bufptr += fc_avail;
		fc_slen -= fc_avail;
	}
}


static int fc_adjust_sign(int fc_is_negative, int fc_forcesign, int *fc_signvalue)
{
	if (fc_is_negative)
	{
		*fc_signvalue = '-';
		return true;
	}
	else if (fc_forcesign)
		*fc_signvalue = '+';
	return false;
}


static int fc_compute_padlen(int fc_minlen, int fc_vallen, int fc_leftjust)
{
	int			fc_padlen;

	fc_padlen = fc_minlen - fc_vallen;
	if (fc_padlen < 0)
		fc_padlen = 0;
	if (fc_leftjust)
		fc_padlen = -fc_padlen;
	return fc_padlen;
}


static void fc_leading_pad(int fc_zpad, int fc_signvalue, int *fc_padlen, PrintfTarget *fc_target)
{
	int			fc_maxpad;

	if (*fc_padlen > 0 && fc_zpad)
	{
		if (fc_signvalue)
		{
			fc_dopr_outch(fc_signvalue, fc_target);
			--(*fc_padlen);
			fc_signvalue = 0;
		}
		if (*fc_padlen > 0)
		{
			fc_dopr_outchmulti(fc_zpad, *fc_padlen, fc_target);
			*fc_padlen = 0;
		}
	}
	fc_maxpad = (fc_signvalue != 0);
	if (*fc_padlen > fc_maxpad)
	{
		fc_dopr_outchmulti(' ', *fc_padlen - fc_maxpad, fc_target);
		*fc_padlen = fc_maxpad;
	}
	if (fc_signvalue)
	{
		fc_dopr_outch(fc_signvalue, fc_target);
		if (*fc_padlen > 0)
			--(*fc_padlen);
		else if (*fc_padlen < 0)
			++(*fc_padlen);
	}
}


static void fc_trailing_pad(int fc_padlen, PrintfTarget *fc_target)
{
	if (fc_padlen < 0)
		fc_dopr_outchmulti(' ', -fc_padlen, fc_target);
}
